﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Windows.Forms;
//using System.Xaml;
using System.Windows.Markup;

namespace RSkor.Common.WinForms
{
    public class MarkupHelper
    {
        private IRootObject _root;
        internal MarkupHelper(IRootObject root)
        {
            _root = root;
        }

        /// <summary>
        /// 
        /// </summary>
        private Dictionary<string, object> _resources = null;

        /// <summary>
        /// 
        /// </summary>
        internal Dictionary<string, object> Resources
        {
            get
            {
                if (_resources == null)
                    _resources = new Dictionary<string, object>();
                return _resources;
            }
        }

        private List<ReferenceExtension> _unresolvedReferences = null;

        internal List<ReferenceExtension> UnresolvedReferences
        {
            get
            {
                if (_unresolvedReferences == null)
                {
                    _unresolvedReferences = new List<ReferenceExtension>();
                }
                return _unresolvedReferences;
            }
        }

        private Control FindControl(string name, IList controls)
        {
            foreach (Control c in controls)
            {
                if (string.Compare(c.Name, name, true) == 0)
                    return c;

                if (c.Controls != null)
                {
                    Control result = FindControl(name, c.Controls);
                    if (result != null)
                        return result;
                }
            }
            return null;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        internal object FindName(string name)
        {
            object result = FindControl(name, _root.Children);
            if (result != null)
                return result;

            if (_resources != null)
            {
                _resources.TryGetValue(name, out result);
            }
            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <param name="scopedElement"></param>
        private static bool FixupElementReference(object root, string name, object scopedElement)
        {
            Type type = root.GetType();
            FieldInfo fi = type.GetField(name, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
            if (fi != null && fi.GetValue(root) == null && fi.FieldType.IsAssignableFrom(scopedElement.GetType()))
            {
                fi.SetValue(root, scopedElement);
                return true;
            }

            return false;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <param name="scopedElement"></param>
        internal void RegisterName(string name, object scopedElement)
        {
            FixupElementReference(_root, name, scopedElement);
            if (scopedElement is Control)
            {
                (scopedElement as Control).Name = name;
                return;
            }

            if (scopedElement is ToolStripItem)
            {
                (scopedElement as ToolStripItem).Name = name;
                Resources.Add(name, scopedElement);
                return;
            }

            if (scopedElement is TreeNode)
            {
                (scopedElement as TreeNode).Name = name;
                return;
            }
        }

        internal void UnregisterName(string name)
        {
            // Do nothing
        }

        internal static Dictionary<Thread, Stack<IRootObject>> LoadingObjects = new Dictionary<Thread, Stack<IRootObject>>();

        /// <summary>
        /// 
        /// </summary>
        /// <param name="root"></param>
        internal static void BeginLoadingRootObject(IRootObject root)
        {
            lock (LoadingObjects)
            {
                if (LoadingObjects.ContainsKey(Thread.CurrentThread))
                {
                    LoadingObjects[Thread.CurrentThread].Push(root);
                }
                else
                {
                    Stack<IRootObject> stack = new Stack<IRootObject>();
                    stack.Push(root);
                    LoadingObjects.Add(Thread.CurrentThread, stack);
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        internal static IRootObject EndLoadingRootObject()
        {
            lock (LoadingObjects)
            {
                IRootObject result = LoadingObjects[Thread.CurrentThread].Pop();
                return result;
            }
        }

        public static object LoadFromMarkup(string uriPath, IDictionary<string, object> arguments)
        {
            // TODO Look at binary folder
            throw new NotImplementedException();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="arguments"></param>
        /// <returns></returns>
        public static object LoadFromMarkup(Stream stream, IDictionary<string, object> arguments)
        {
            //System.Xaml.XamlReader xr = new System.Xaml.XamlXmlReader(stream);
            //while (xr.Read())
            //{ 
            //}
            //object result = xr.Value;
            object result = XamlReader.Load(stream);
            if (result is IRootObject)
            {
                if (arguments != null)
                {
                    foreach (KeyValuePair<string, object> kv in arguments)
                    {
                        if ((result as IRootObject).Resources.ContainsKey(kv.Key))
                        {
                            (result as IRootObject).Resources[kv.Key] = kv.Value;
                        }
                        else
                        {
                            (result as IRootObject).Resources.Add(kv.Key, kv.Value);
                        }
                    }
                }

                foreach (KeyValuePair<string, object> kv in (result as IRootObject).Resources)
                {
                    FixupElementReference(result, kv.Key, kv.Value);
                }

                if ((result as IRootObject).UnresolvedReferences != null)
                {
                    foreach (ReferenceExtension refer in (result as IRootObject).UnresolvedReferences)
                    {
                        if (!refer.ResolveReference(result as IRootObject))
                        {
                            // TODO Diff exception type.
                            throw new ArgumentException();
                        }
                    }
                }

                if (LoadingObjects.ContainsKey(Thread.CurrentThread)
                    && object.ReferenceEquals(LoadingObjects[Thread.CurrentThread].Peek(), result))
                {
                    EndLoadingRootObject();
                }
            }
            return result;
        }
    }
}
